package org.yenbay.timer;

import java.net.URL;
import java.net.URLConnection;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * Created by liuyb at 2021-06-15 18:05:09 <br> 获取服务器的时间，设置为系统时间
 *
 */
public class TimeSync {

	private static final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();


	public static void main(String[] args) {
		ArgumentParser parser = ArgumentParser.build(args);
		// 模式，精准模式，时间严格按照指定服务器，精确时间同步
		final boolean isStrict = parser.getComplexBool("strict");
		log("同步模式为：%s", isStrict ? "精准模式" : "普通模式");
		// 频率，同步间隔
		Integer rate = parser.getNonNull("rate", 3600);
		log("同步时间频率为：" + rate + "秒/次");
		// 网络服务器
		String server = parser.get("server");
		List<String> servers = new ArrayList<>(16); 
		servers.add("https://www.baidu.com");
		servers.add("https://cn.bing.com"); 
		// 要同步的服务器
		if(server != null && server.trim().length() > 0) { 
			if(isStrict) {
				servers.clear();
			}
			log("增加网络时间服务器地址：" + server); 
			servers.add(server);
		} 
		// 执行
		executorService.scheduleAtFixedRate(new Runnable() {

			@Override
			public void run() { 
				log("开始执行时间同步...");
				// 从网络上获取时间
				for (int i = servers.size() - 1; i >= 0; i--) {
					long millis = getTimeInMillis(servers.get(i));
					if (millis <= 0) {
						continue;
					}
					// 修改本机时间
					log("设置前的时间差为 %s 毫秒", millis - System.currentTimeMillis());
					setTimeInMillis(millis, isStrict);  
					log("设置后的时间差为 %s 毫秒", millis - System.currentTimeMillis());
					return;
				} 
				err("未从时间服务器获取到时间");
			}

		}, 2, rate, TimeUnit.SECONDS); 
	}

	/**
	 * 通过请求远程服务器网址，获取对方头部时间
	 * 
	 * @param server
	 * @return
	 */
	private static long getTimeInMillis(String server) { 
		if(server == null || server.trim().length() == 0) {
			return -1;
		}
		try { 
			log("开始访问时间服务器：%s", server);
			URLConnection conn = new URL(server).openConnection();
			String dateStr = conn.getHeaderField("Date");
			// 解析时间
			DateFormat httpDateFormat = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z", Locale.US);
			httpDateFormat.setTimeZone(TimeZone.getDefault());
			Date date = httpDateFormat.parse(dateStr); 
			log("获取到的服务器时间：%s" , date.toString());  
			return date.getTime();
		}catch(Exception e) { 
		} 
		return -1;  
	}


	/**
	 * 通过调用本地命令date和time修改计算机时间
	 * 
	 * @param millis
	 */
	private static void setTimeInMillis(long millis, boolean strict) {
		Calendar c = Calendar.getInstance(TimeZone.getDefault());
		c.setTimeInMillis(millis);
		int year = c.get(Calendar.YEAR);
		int month = c.get(Calendar.MONTH) + 1;
		int day = c.get(Calendar.DAY_OF_MONTH);
		int hour = c.get(Calendar.HOUR_OF_DAY);
		int minute = c.get(Calendar.MINUTE);
		int second = c.get(Calendar.SECOND);
		c.setTime(new Date());
		int year_c = c.get(Calendar.YEAR);
		int month_c = c.get(Calendar.MONTH) + 1;
		int day_c = c.get(Calendar.DAY_OF_MONTH);
		int hour_c = c.get(Calendar.HOUR_OF_DAY);
		int minute_c = c.get(Calendar.MINUTE);
		int second_c = c.get(Calendar.SECOND);
		String ymd = year + "-" + month + "-" + day;
		String time = hour + ":" + minute + ":" + second; 
		try {
			// 日期不一致就修改一下日期
			if (strict || year != year_c || month != month_c || day != day_c) {
				String cmd = "cmd /c date " + ymd;
				Process process = Runtime.getRuntime().exec(cmd);
				process.waitFor(); 
			}
			// 时间不一致就修改一下时间
			if (strict || hour != hour_c || minute != minute_c || Math.abs(second - second_c) > 1) {
				String cmd = "cmd  /c  time " + time;
				Process process = Runtime.getRuntime().exec(cmd);
				process.waitFor();
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	} 

	private synchronized static void log(String format, Object... args) {
		System.out.println(format == null ? "null" : String.format(format.replace("{}", "%s"), args));
	}

	private synchronized static void err(String format, Object... args) {
		System.err.println(format == null ? "null" : String.format(format.replace("{}", "%s"), args));
	}
}
